//
// Created by mio on 25-4-10.
//

#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

#include "TcpSocket.h"


void SocketUtil::SetNonBlock(int sockfd)
{
    // 调用 fcntl 系统调用，使用 F_GETFL 命令获取 sockfd 当前的文件状态标志（flags）
    int flags = fcntl(sockfd, F_GETFL, 0);
    /* 使用按位或 (|) 操作符，在获取到的 flags 上添加 O_NONBLOCK 标志,
     * 再次调用 fcntl，使用 F_SETFL 命令，将修改后的 flags（包含了 O_NONBLOCK）设置回给 sockfd */
    fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
}

void SocketUtil::SetBlock(int sockfd)
{
    int flags = fcntl(sockfd, F_GETFL, 0);
    fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK));
}

void SocketUtil::SetReuseAddr(int sockfd)
{
    int on = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
}

void SocketUtil::SetReusePort(int sockfd)
{
    int on = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on));
}

void SocketUtil::SetKeepAlive(int sockfd)
{
    int on = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));
}

void SocketUtil::SetSendBufSize(int sockfd, int size)
{
    setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
}

void SocketUtil::SetRecvBufSize(int sockfd, int size)
{
    setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
}

// ----

TcpSocket::TcpSocket()
{

}

TcpSocket::~TcpSocket()
{

}

int TcpSocket::Create()
{
    sockfd_ = ::socket(AF_INET, SOCK_STREAM, 0);
    return sockfd_;
}

bool TcpSocket::Bind(std::string ip, short port)
{
    sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr(ip.c_str());
    addr.sin_port = htons(port);
    if (::bind(sockfd_, (sockaddr*)&addr, sizeof(sockaddr)) == -1)
    {
        return false;
    }
    return true;
}

bool TcpSocket::Listen(int backlog)
{
    if (sockfd_ == -1)
        return false;
    if (::listen(sockfd_, backlog) == -1)
        return false;
    return true;
}

int TcpSocket::Accept()
{
    sockaddr_in addr = {0};
    socklen_t addrlen = sizeof(addr);
    return ::accept(sockfd_, (sockaddr*)&addr, &addrlen);
}

void TcpSocket::Close()
{
    if (sockfd_ != -1)
    {
        ::close(sockfd_);
        sockfd_ = -1;
    }
}

void TcpSocket::ShutdownWrite()
{
    if (sockfd_ != -1)
    {
        /* 作用：
         *  - 该套接字不能再发送数据。任何后续的 send 或 write 调用会失败
         *  - 内核会向对方发送一个 FIN 包，表示本端的数据发送完毕
         *  - 该套接字仍然可以接收数据。可以继续调用 recv 或 read 来读取对方可能仍在发送的数据
         */
        shutdown(sockfd_, SHUT_WR); // SHUT_WR: 一个常量，指示要关闭连接的写入方向
    }
}
